package au.com.zonski.gwlite.j2me.client;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.game.GameCanvas;

import net.jscience.math.kvm.MathFP;


public class PlayerController implements Controller 
{
	private static final int DEFAULT_PLAYER_LIVES = 3;
	private static final int DEFAULT_PLAYER_BOMBS = 3;
	
	public static final byte TURRET_ROTATION = 1;
	private static final int BULLET_SPREAD = MathFP.PI / 16;
	/*
	private static final int[] SPREADS = {
		0,
		-BULLET_SPREAD/3, 
		BULLET_SPREAD/4, 
		-BULLET_SPREAD/4,
		BULLET_SPREAD/2,
		-BULLET_SPREAD/2, 
		BULLET_SPREAD/3, 
	};
	*/
	private static final int[] SPREADS = {
		-BULLET_SPREAD/2, 
		BULLET_SPREAD/4,
		-BULLET_SPREAD/4, 
		BULLET_SPREAD/2,
	};
	private static final int MAX_BULLETS = 4;

	
	private static final byte NORTH = 0;
	private static final byte SOUTH = 1;
	private static final byte EAST  = 2;
	private static final byte WEST  = 3;
	private static final byte NORTH_EAST = 4;
	private static final byte NORTH_WEST = 5;
	private static final byte SOUTH_EAST = 6;
	private static final byte SOUTH_WEST = 7;
	
	private int[][] MULTIPLIERS = new int[][]{
			{ 0, -MathFP.toFP(1) },
			{ 0, MathFP.toFP(1) },
			{ MathFP.toFP(1), 0 },
			{ -MathFP.toFP(1), 0 },
			{ MathFP.sqrt( MathFP.toFP(2) ), -MathFP.sqrt( MathFP.toFP(2) ) },
			{ -MathFP.sqrt( MathFP.toFP(2) ), -MathFP.sqrt( MathFP.toFP(2) ) },
			{ MathFP.sqrt( MathFP.toFP(2) ), MathFP.sqrt( MathFP.toFP(2) ) },
			{ -MathFP.sqrt( MathFP.toFP(2) ), MathFP.sqrt( MathFP.toFP(2) ) }
	};
	private int TURN_SPEED = MathFP.PI / 16;
	public int SPEED = MathFP.toFP( 5 );
	
	public int rotateCWKey;
	public int rotateCCWKey;
	public int forwardKey;
	public int backKey;
	
	public int eastKey;
	public int westKey;
	public int northKey;
	public int southKey;
	public int northEastKey;
	public int northWestKey;
	public int southEastKey;
	public int southWestKey;

	public int toggleAutoFireKey;
	public int bombKey;

	public int turretLockKey;
	public int turretRotateCWKey;
	public int turretRotateCCWKey;
	
	
	public int turretEastKey;
	public int turretWestKey;
	public int turretNorthKey;
	public int turretSouthKey;
	public int turretNorthEastKey;
	public int turretNorthWestKey;
	public int turretSouthEastKey;
	public int turretSouthWestKey;
	
	private int score = 0;
	
	private boolean gameActionsTurret;
	private boolean gameActionsMove;
	
	public int playerLives;
	public int playerBombs;
	private int bulletsMod;
	//private int bullets = 12;
	private int shotCount;
	private int shots;
	private int fireRate = SPREADS.length;
	public int bulletSpeed = 12;
	private int nextBullet = fireRate;
	public int lastBomb;
	public int[] lastBombPosition;
	
	private int powerUpLevel;
	private int nextPowerUpIncrement;
	public int nextPowerUpScore;
	public int multiplier;
	public int multiplierCountDown;
	public int multiplierCount;
	public int nextLifeScore;
	public int nextLifeIncrement;
	public int nextBombScore;
	public int nextBombIncrement;
	
	
	private boolean north;
	private boolean south;
	private boolean east;
	private boolean west;
	private boolean southEast;
	private boolean southWest;
	private boolean northEast;
	private boolean northWest;
	
	private boolean rotateCW;
	private boolean rotateCCW;
	private boolean forward;
	private boolean back;
	
	private boolean turretNorth;
	private boolean turretSouth;
	private boolean turretEast;
	private boolean turretWest;
	private boolean turretSouthEast;
	private boolean turretSouthWest;
	private boolean turretNorthEast;
	private boolean turretNorthWest;

	private boolean turretRotateCW;
	private boolean turretRotateCCW;

	private boolean bomb;
	private boolean turretLock;
	private boolean autofire = true;
	
	private boolean targetting;
	private int targetX;
	private int targetY;
	
	public PlayerController( Canvas canvas )
	{
		// movement
		this.gameActionsMove = false;
		
		this.eastKey = Canvas.KEY_NUM6;
		this.westKey = Canvas.KEY_NUM4;
		this.northKey = Canvas.KEY_NUM2;
		this.southKey = Canvas.KEY_NUM8;
		this.northEastKey = Canvas.KEY_NUM3;
		this.northWestKey = Canvas.KEY_NUM1;
		this.southEastKey = Canvas.KEY_NUM9;
		this.southWestKey = Canvas.KEY_NUM7;
		
		this.rotateCCWKey = Canvas.KEY_STAR;
		this.rotateCWKey = Canvas.KEY_POUND;
		this.forwardKey = Canvas.KEY_NUM0;

		// turret
		this.gameActionsTurret = false;

		this.turretEastKey = canvas.getKeyCode( GameCanvas.RIGHT );
		this.turretWestKey = canvas.getKeyCode( GameCanvas.LEFT );
		this.turretNorthKey = canvas.getKeyCode( GameCanvas.UP );
		this.turretSouthKey = canvas.getKeyCode( GameCanvas.DOWN );
		this.turretLockKey = canvas.getKeyCode( GameCanvas.FIRE );
		
		// other
		//this.toggleAutoFireKey = Canvas.KEY_NUM0;
		this.bombKey = Canvas.KEY_NUM5;
	}
	
	public void reset() 
	{
		// reset key states
		/*
		this.east = false;
		this.west = false;
		this.north = false;
		this.south = false;
		this.northEast = false;
		this.southEast = false;
		this.northWest = false;
		this.southWest = false;
		
		this.rotateCCW = false;
		this.rotateCW = false;
		this.forward = false;
		this.back = false;
		
		this.turretEast = false;
		this.turretWest = false;
		this.turretNorth = false;
		this.turretSouth = false;
		this.turretNorthEast = false;
		this.turretSouthEast = false;
		this.turretNorthWest = false;
		this.turretSouthWest = false;
		
		this.turretRotateCCW = false;
		this.turretRotateCW = false;
		
		this.bomb = false;
		*/
		//this.turretLock = false;
		//this.autofire = true;
		
		// reset lives
		this.playerLives = DEFAULT_PLAYER_LIVES;
		// reset bombs
		this.playerBombs = DEFAULT_PLAYER_BOMBS;
		
		this.score = 0;
		this.nextPowerUpIncrement = 500;
		this.nextPowerUpScore = this.nextPowerUpIncrement;
		this.powerUpLevel = 1;
		this.nextLifeScore = 40000;
		this.nextLifeIncrement = 40000;
		this.nextBombScore = 20000;
		this.nextBombIncrement = 40000;
		
		this.lastBomb = 0;
		this.lastBombPosition = null;
		
		resetMultiplier();
	}
	
	private void resetMultiplier()
	{
		this.multiplierCount = 50;
		this.multiplierCountDown = this.multiplierCount;
		this.multiplier = 1;
	}

	public boolean update(GWLiteCanvas gameState, GWEntity o) 
	{
		int[] controllerData = (int[]) o.controllerData;
		
		if( controllerData == null )
		{
			controllerData = new int[]{ 0, 0 };
			o.controllerData = controllerData;
		}
		
		int currentRotation = controllerData[ 0 ];
		
		int dx = 0;
		int dy = 0;
		
		if( this.east )
		{
			dx += MULTIPLIERS[ EAST ][0];
			dy += MULTIPLIERS[ EAST ][1];
		}
		if( this.west )
		{
			dx += MULTIPLIERS[ WEST ][0];
			dy += MULTIPLIERS[ WEST ][1];
		}
		if( this.south )
		{
			dx += MULTIPLIERS[ SOUTH ][0];
			dy += MULTIPLIERS[ SOUTH ][1];
		}
		if( this.north )
		{
			dx += MULTIPLIERS[ NORTH ][0];
			dy += MULTIPLIERS[ NORTH ][1];
		}
		if( this.southEast )
		{
			dx += MULTIPLIERS[ SOUTH_EAST ][0];
			dy += MULTIPLIERS[ SOUTH_EAST ][1];
		}
		if( this.southWest )
		{
			dx += MULTIPLIERS[ SOUTH_WEST ][0];
			dy += MULTIPLIERS[ SOUTH_WEST ][1];
		}
		if( this.northWest )
		{
			dx += MULTIPLIERS[ NORTH_WEST ][0];
			dy += MULTIPLIERS[ NORTH_WEST ][1];
		}
		if( this.northEast )
		{
			dx += MULTIPLIERS[ NORTH_EAST ][0];
			dy += MULTIPLIERS[ NORTH_EAST ][1];
		}
		if( dx != 0 || dy != 0 )
		{
			// work out the rotation
			currentRotation = GWLiteCanvas.atan2( dy, dx );
		}
		if( this.rotateCCW )
		{
			currentRotation += TURN_SPEED; 
		}
		if( this.rotateCW )
		{
			currentRotation -= TURN_SPEED;
		}
		if( this.forward )
		{
			dx += MathFP.cos( currentRotation );
			dy += MathFP.sin( currentRotation );
		}
		if( this.back )
		{
			dx -= MathFP.cos( currentRotation );
			dy -= MathFP.sin( currentRotation );
		}
		
		int length = MathFP.sqrt( (dx*dx + dy*dy) >> MathFP.getPrecision() );
		
		if( length > 0 )
		{
			dx = (SPEED * dx) / length;
			dy = (SPEED * dy) / length;
		
			if(! gameState.slowing )
			{
				GWEntity thrust = new GWEntity( o.x + o.width/2, o.y + o.height/2, 0, 0, GWEntity.TYPE_THRUST );
				gameState.layers[ gameState.layers.length - 2 ].addElement( thrust );
			}
			
			o.x += dx;
			o.y += dy;
			
			gameState.enforceBounds( o );
			
			gameState.viewX = 
				Math.min(
						gameState.levelWidth - gameState.screenWidth,
						Math.max( 0, o.x - (gameState.screenWidth - o.width)/2 )
				);
			gameState.viewY = 
				Math.min(
						gameState.levelHeight - gameState.screenHeight,
						Math.max( 0, o.y - (gameState.screenHeight - o.height)/2 )
				);
			
		}
		
		int turretRotation = controllerData[ TURRET_ROTATION ]; 
		
		// handle turret
		if( this.turretLock )
		{
			// it's rotation is the same as the current rotation
			turretRotation = currentRotation;
		}
		else if( this.targetting )
		{
			int tx = MathFP.toFP( this.targetX ) + gameState.viewX;
			int ty = MathFP.toFP( this.targetY ) + gameState.viewY;
			int px = o.x + o.width/2;
			int py = o.y + o.height/2;
			int tdx = tx - px;
			int tdy = ty - py;
			if( tdx != 0 || tdy != 0)
			{
				turretRotation = GWLiteCanvas.atan2( tdy, tdx );
			}
		}
		else
		{	
			int targetAngle = 0;
			int angleCount = 0;
			if( this.turretEast )
			{
				if( !this.turretWest )
				{
					if( this.turretNorth || this.turretNorthEast || this.turretNorthWest )
					{
						targetAngle += MathFP.PI * 2;
					}
					else
					{
						targetAngle += 0;
					}
					angleCount ++;
				}
			}
			if( this.turretWest )
			{
				if( !this.turretEast )
				{
					targetAngle += MathFP.PI;
					angleCount++;
				}
			}
			if( this.turretNorth )
			{
				if( !this.turretSouth )
				{
					targetAngle +=  (3 * MathFP.PI) / 2;
					angleCount++;
				}
			}
			if( this.turretSouth )
			{
				if( !this.turretNorth )
				{
					targetAngle += MathFP.PI / 2;
					angleCount++;
				}
			}
			if( this.turretNorthEast )
			{
				if( !this.turretSouthWest )
				{
					targetAngle += (7 * MathFP.PI) / 4;
					angleCount++;
				}
			}
			if( this.turretNorthWest )
			{
				if( !this.turretSouthEast )
				{
					targetAngle += (5 * MathFP.PI) / 4;
					angleCount++;
				}
			}
			if( this.turretSouthEast )
			{
				if( !this.turretNorthWest )
				{
					targetAngle += MathFP.PI / 4;
					angleCount++;
				}
			}
			if( this.turretSouthWest )
			{
				if( !this.turretNorthEast )
				{
					targetAngle += (3 * MathFP.PI) / 4;
					angleCount++;
				}
			}
			if( angleCount > 0 )
			{
				targetAngle /= angleCount;
//				targetAngle %= (MathFP.PI * 2 );
//				
//				while( targetAngle < turretRotation )
//				{
//					targetAngle += MathFP.PI * 2;
//				}
				targetAngle %= MathFP.PI*2;
				while( turretRotation < targetAngle )
				{
					turretRotation += MathFP.PI * 2;
				}
				//turretRotation %= MathFP.PI*2;
				int diff = targetAngle - turretRotation + MathFP.PI * 2;
				
				if( diff >= (3*MathFP.PI)/4 && diff <= (5*MathFP.PI)/4 )
				{
					turretRotation = targetAngle;
				}
				else if( diff > MathFP.PI )
				{
					turretRotation -= Math.min(TURN_SPEED, MathFP.PI * 2 - diff);
				}
				else
				{
					turretRotation += Math.min( TURN_SPEED, diff );
				}
				turretRotation = turretRotation % (MathFP.PI*2);
			}
		}
		if( this.bomb )
		{
			if( this.playerBombs > 0 )
			{
				this.lastBomb = gameState.updateNumber;
				this.lastBombPosition = new int[]{ o.x + o.width/2, o.y + o.height/2 };
				this.playerBombs --;
				this.bomb = false;
				gameState.setExplodeAll( o );
				this.resetMultiplier();
			}
		}
		if( this.autofire && gameState.updateNumber % 3 == 0)
		{
			// add bullets
			//int bulletsPerRound = this.score / 2048 + this.bulletsMod + 4;
			int bulletsPerRound = this.powerUpLevel + this.bulletsMod;
			int bullets;
			int startAngle;
			int deltaAngle;
			int mod = this.shotCount % this.fireRate; 
			bullets = Math.min( MAX_BULLETS, bulletsPerRound / this.fireRate );
			this.bulletsMod = bulletsPerRound % this.fireRate;
			this.shotCount++;
			if( bullets > 0 )
			{
				startAngle = SPREADS[this.shots % SPREADS.length];
				this.shots ++;
				if( bullets == 1 )
				{
					deltaAngle = 0;
				}
				else 
				{
					if( startAngle == 0 )
					{
						startAngle = BULLET_SPREAD/2;
					}
					deltaAngle = -(startAngle * 2) / (bullets-1);
				}
				startAngle += turretRotation;
				for( int i=0; i<bullets; i++ )
				{
					//int rotation = startAngle + i * BULLET_SPREAD;
					int rotation = startAngle;
					startAngle += deltaAngle;
					GWEntity bullet = new GWEntity(
							gameState,
							o.x + o.width/2, 
							o.y + o.height/2, 
							GWEntity.TYPE_BULLET
					);
					bullet.controllerData = new int[]{  
							rotation,
							MathFP.cos( rotation ) * this.bulletSpeed, 
							MathFP.sin( rotation ) * this.bulletSpeed
					};
					gameState.layers[GWLiteCanvas.BULLET_LEVEL].addElement( bullet );
				}
			}
			this.nextBullet = this.fireRate;
		}
		this.nextBullet --;
		
		controllerData[ 0 ] = currentRotation;
		controllerData[ TURRET_ROTATION ] = turretRotation;
		// o.controllerData = controllerData; - unnecessary
		
		return false;
	}

	public void learn(GWLiteCanvas gameState) 
	{
		if( this.gameActionsMove || this.gameActionsTurret )
		{
			int gameActions = gameState.getKeyStates();
			if( this.gameActionsMove )
			{
				this.south = ( (gameActions & this.southKey) != 0 );
				this.north = ( (gameActions & this.northKey) != 0 );
				this.east = ( (gameActions & this.eastKey) != 0 );
				this.west = ( (gameActions & this.westKey) != 0 );
				
				this.rotateCW = ( (gameActions & this.rotateCWKey) != 0 );
				this.rotateCCW = ( (gameActions & this.rotateCCWKey) != 0 );
			}
			if( this.gameActionsTurret )
			{
				this.turretSouth = ( (gameActions & this.turretSouthKey) != 0 );
				this.turretNorth = ( (gameActions & this.turretNorthKey) != 0 );
				this.turretEast = ( (gameActions & this.turretEastKey) != 0 );
				this.turretWest = ( (gameActions & this.turretWestKey) != 0 );
				
				this.turretRotateCW = ( (gameActions & this.turretRotateCWKey) != 0 );
				this.turretRotateCCW = ( (gameActions & this.turretRotateCCWKey) != 0 );
			}
			// TODO : bomb and turret lock might want to go in here
		}
		
	}

	public boolean collision(GWLiteCanvas gameState, GWEntity controlled, GWEntity with) 
	{
		// die
		this.playerLives --;
		if( this.playerLives >= 0 )
		{
			this.resetMultiplier();
			this.powerUpLevel = Math.max( 1, this.powerUpLevel - 2 );
			gameState.setExplodeAll( with );
			gameState.lastDeath = gameState.updateNumber;
			this.lastBomb = gameState.updateNumber;
			this.lastBombPosition = new int[]{ 
					controlled.x + controlled.width/2, 
					controlled.y + controlled.height/2
			};
		}
		return true;
	}

	public void setKeyState( int keyCode, boolean down )
	{
		if( !this.gameActionsMove )
		{
			if( keyCode == this.eastKey )
			{
				this.east = down;
			}
			else if( keyCode == this.westKey )
			{
				this.west = down;
			}
			else if( keyCode == this.southKey )
			{
				this.south = down;
			}
			else if( keyCode == this.northKey )
			{
				this.north = down;
			}
			else if( keyCode == this.northEastKey )
			{
				this.northEast = down;
			}
			else if( keyCode == this.northWestKey )
			{
				this.northWest = down;
			}
			else if( keyCode == this.southEastKey )
			{
				this.southEast = down;
			}
			else if( keyCode == this.southWestKey )
			{
				this.southWest = down;
			}
			else if( keyCode == this.rotateCCWKey )
			{
				this.rotateCCW = down;
			}
			else if( keyCode == this.rotateCWKey )
			{
				this.rotateCW = down;
			}
			else if( keyCode == this.forwardKey )
			{
				this.forward = down;
			}
			else if( keyCode == this.backKey )
			{
				this.back = down;
			}
		}
		if( !this.gameActionsTurret )
		{
			if( keyCode == this.turretEastKey )
			{
				this.turretEast = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretWestKey )
			{
				this.turretWest = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretSouthKey )
			{
				this.turretSouth = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretNorthKey )
			{
				this.turretNorth = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretNorthEastKey )
			{
				this.turretNorthEast = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretNorthWestKey )
			{
				this.turretNorthWest = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretSouthEastKey )
			{
				this.turretSouthEast = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretSouthWestKey )
			{
				this.turretSouthWest = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretRotateCCWKey )
			{
				this.turretRotateCCW = down;
				this.autofire = true;
			}
			else if( keyCode == this.turretRotateCWKey )
			{
				this.turretRotateCW = down;
				this.autofire = true;
			}
		}
		if( keyCode == this.bombKey )
		{
			this.bomb = down;
		}
		if( keyCode == this.turretLockKey )
		{
			this.turretLock = !this.turretLock;
		}
		if( keyCode == this.toggleAutoFireKey )
		{
			this.autofire = !this.autofire;
		}
	}	
	
	public void target( int x, int y )
	{
		this.targetting = true;
		this.targetX = x;
		this.targetY = y;
	}
	
	public void untarget( int x, int y )
	{
		this.targetting = false;
	}
	
	public int getScore()
	{
		return this.score;
	}
	
	public void addScore( int amount )
	{
		this.score += amount;
		if( this.score >= this.nextPowerUpScore )
		{
			this.score += amount;
			this.nextPowerUpScore += this.nextPowerUpIncrement;
			this.nextPowerUpIncrement *= 2;
			this.powerUpLevel++;
		}
		if( this.score >= this.nextLifeScore )
		{
			this.nextLifeScore += this.nextLifeIncrement;
			this.playerLives++;
		}
		if( this.score >= this.nextBombScore )
		{
			this.nextBombScore += this.nextBombIncrement;
			this.playerBombs++;
		}
		this.multiplierCountDown--;
		if( this.multiplierCountDown < 0 )
		{
			this.multiplierCount *= 2;
			this.multiplierCountDown = this.multiplierCount;
			this.multiplier++;
		}
				
	}
}
